TODO

library(ggplot2)
library(data.table)
library(dplyr)

Load all good candidates for all GAGA species (1st filter: Take only good candidates from both databases)

Load all tsv files for the different GAGA ids in a list of data frames.

#tsv2load<-"/Users/lukas/sciebo/Projects/LGT/results/GAGA.LGT/*/results/*.*.lgt.good.candidates.tsv"
tsv2load<-"/Users/Janina/sciebo/GAGA.LGT/*/results/*.*.lgt.good.candidates.tsv"
#tsv2load<-"/global/scratch2/j_rink02/master/lgt/0_data/*/results/*.*.lgt.good.candidates.tsv"
dataFiles <- lapply(Sys.glob(tsv2load), read.csv,sep="\t")

add the name

Add the GAGA id as a name to the different list elements. Add the database origin as a name to the different list elements. Set the GAGA-id and database origin separated with a “.” as a name for each element of the list “dataFiles”. Each element in the list is one genome, either from the noAnt or the eukaryotic database including the candidates.

ids<-gsub(".*/(.+-[0-9]+)\\..+.lgt.good.candidates.tsv","\\1",Sys.glob(tsv2load))
type<-gsub(".*/(.+-[0-9]+)\\.(.+).lgt.good.candidates.tsv","\\2",Sys.glob(tsv2load))
names(dataFiles)<-paste(ids,type,sep=".")

Combine all GAGA ids

Combine tsv files from all GAGA ids in the large list element (“dataFiles”) to one big data frame (“df”).


df<-rbindlist(dataFiles,idcol = T, fill=TRUE) #the fill=TRUE option fills missing columns 

The unfiltered df containes 13664 candidates from all 162 selected genomes together. Each row in the dataframe represents one candidate.

head(df)

Add scaffold length for every genome

  1. Add the scaffolds with their lengths for every genome from the file “genome.file”.
  2. Create a large dataframe with scaffold length information for every genome called “dscf”
  3. Merge the scaffold information together with the information for the LGTs
#data2load<-"/Users/lukas/sciebo/Projects/LGT/results/GAGA.LGT/*/results/genome.file"
data2load<-"/Users/Janina/sciebo/GAGA.LGT/*/results/genome.file"  # load data from all genome.files
#data2load<-"/global/scratch2/j_rink02/master/lgt/0_data/*/results/genome.file"  # load data from all genome.files
genomeFiles <- lapply(Sys.glob(data2load), read.csv,sep="\t",header=F) # create large list of all genome.files

# Create a large df with information on scaffold number and length for every genome
genome_ids<-gsub(".*\\/(.*)\\/results\\/genome.file","\\1",Sys.glob(data2load))
names(genomeFiles)<-paste(genome_ids)

dscf<-rbindlist(genomeFiles,idcol = T)

colnames(dscf)
[1] ".id" "V1"  "V2" 
names(dscf)[2]<-"cand.scaffold" #rename the second column of dscf to merge later by this column
names(dscf)[1]<-"GAGA_id"

df$GAGA_id<-gsub("\\..*","",df$.id) #separate the first row of df to merge by column "GAGA_id"


# Merge dscf together with the information for every LGT candidate from df
df<-left_join(df, dscf, by = c("GAGA_id","cand.scaffold"))
names(df)[55]<-"scaffold.length"
df$V2<- NULL
Column 'V2' does not exist to remove
head(df)

extract eval

  1. Extract the evalue of the best prokaryotic hit (take column “bestProHit”. In this column, the evalue is located
    between other values and will be extracted in the next command). Make a new data table (called “bestblasthits”), separating the original column bestProHit into different columns, with the each value being separated by “;”.

  2. Add a column called “besteval” to the df, taking only the eval (column V4) from the bestblasthits dataframe.

bestblasthits<-read.csv(text=as.character(df$bestProHit),sep = ";",as.is = T,fill = T,blank.lines.skip = F,header=F)
df$besteval<-bestblasthits$V4

add column with length of candidate

df$cand.length<-df$cand.end-df$cand.start

extract broad locus start and stop

To cluster candidates in one close region together and view them as one single LGT, take the broad locus.

  1. The broad locus (columns “scaffold”, start“,”end" or summarized together in column “locus” of df) clusters all candidates within +/- 20kb region together and assigns them the same start and stop codon. Thereby, single candidates can be grouped by the same start and stop codon in the broad locus region. Separate the column “locus” from the df into scaffold (V1), start (V2) and end (V3) in the new dataframe “loci”.

  2. Add a column called “locus.start”, “locus.end” and “locus.length” to the df, taking the now separated columns from the new dataframe “loci”.

loci<-read.csv(text=gsub(":","-",df$locus),sep = "-",as.is = T,fill = T,blank.lines.skip = F,header=F)
df$locus.start<-loci$V2
df$locus.end<-loci$V3
df$locus.length<-df$locus.end-df$locus.start

plot overview plots

plot histograms of different metrics to see how they are distributed.

# gc-content
ggplot(df, aes(x=gc)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# entropy (ce)
ggplot(df, aes(x=ce)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# ct4
ggplot(df, aes(x=ct4)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# ct6
ggplot(df, aes(x=ct6)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# length of broad locus
ggplot(df, aes(x=locus.length)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# length of candidate
ggplot(df, aes(x=cand.length)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# log of best e-value
df$log.besteval<- -log(df$besteval,10)
df$log.besteval[df$log.besteval==Inf]<-max(df$log.besteval[df$log.besteval!=Inf])+1
ggplot(df, aes(x=df$log.besteval)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()


# BitDiffSum
ggplot(df, aes(x=BitDiffSum)) +
  geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

Filter the candidate dataset to exclude false-positives and missassemblies

Filter by evalue (evalue > 1e-25), ct4 (ct4>0.25), ce (ce>1.5), BitDiffSum (BitDiffSum>150) and the length of the candidate (length>100)

dfFilter<-subset(df,log.besteval>25 & ct4>0.25 & ce>1.5 & BitDiffSum>150 & cand.end-cand.start>100)

Filtering by the above criteria reduces the number of candidates to 5355 candidates (originally: 13664) in all 162 genomes.

Remove anything at the beginning of a scaffold and candidates with a length over 20 kb. These are more likely to be misassemblies. (Note that this still leaves in candidates at the very end of scaffolds, which also are more likely misassemblies).

dfFilter2<-subset(dfFilter,locus.start > 1000 & cand.length < 20000)

Filtering by the above criteria reduces the number of candidates further to 3631 candidates in all 162 genomes.

Remove anything at the end of a scaffold.

# Filter the dataframe to remove candidates at the end of the scaffold       
dfFilter3<-subset(dfFilter2, cand.end != scaffold.length)

This further reduces the number of candidates to 3610 possible LGTs in all 162 genomes.

Summarize candidates in close proximity into one larger locus

One row per broad start/stop coordinate

required libraries

library(dplyr)
library(ggrepel)
  1. Create a data frame that has in each row one larger locus (often containing several lgt candidates).
# keep one row per larger locus and paste together all the info for the different LGT candidates contained in this locus

## https://stackoverflow.com/questions/40033625/concatenating-all-rows-within-a-group-using-dplyr/40033725
dfC <- dplyr::group_by(df, locus) %>%
        dplyr::summarise_each(funs(paste(unique(.), collapse = ";")))

# filter this by locus dataframe to only keep loci that have at least one good candidate (i.e. that was contained in the dfFilter3 dataframe)
dfC.filtered<-subset(dfC,locus %in% unique(dfFilter3$locus),select=c(locus,.id,cand.locus,cand.start,cand.end,bestProHit,BitDiffSum,scaffold,start,end,gc,gcs,ce))

# save data frame to file
#write.table(dfC.filtered,"/Users/lukas/sciebo/Projects/LGT/results/GAGA.LGT.filtered.tsv",sep="\t",quote = F,row.names = F)
write.table(dfC.filtered,"/global/scratch2/j_rink02/master/lgt/2_analysis/GAGA.LGT.162.filtered.tsv",sep="\t",quote = F,row.names = F)

# for plotting
# split the unfiltered large df dataframe by "locus" into a list of dataframes (one list element per locus)
dfSplit<-split(df,f=paste(df$.id,df$locus,sep="."))

# filter the list of dataframes to only retain those that contain a LGT from the dfFilter3 dataframe
dfSplit.filtered<-dfSplit[unique(paste(dfFilter3$.id,dfFilter3$locus,sep="."))]

Merging candidates in close proximity together and filtering by the above criteria to only obtain candidates which are also in the dfFilter3 dataframe reduces the number of candidates further to 928 candidates in all 162 genomes.

Note: The filtered.tsv file only contains candidates, which remain after filtering, but the simple plots created by the above commands contain all candidates in this region, even if they would have been filtered out before.

Plot each locus

# Define a function containing a ggplot command. This function will be applied to each element of dfSplit.filtered (the list of dataframes)
plotLGTlocus<-function(locusData){
    locusData$logeval<- -log(locusData$besteval,10)
    locusData$logeval[!is.finite(locusData$logeval)]<- 350
    locusData$species<-substr(gsub(".*;","",locusData$bestProHit),1,20)
    ggplotLGT<-ggplot(locusData)+
          geom_rect(aes(xmin=cand.start,xmax=cand.end,ymin=1,ymax=0,fill=logeval),size=0)+
          coord_cartesian(xlim=c(min(locusData$locus.start),max(locusData$locus.end)),ylim=c(0,5))+
          geom_text_repel(
            aes(x=cand.start,y=1,label=species),
            force_pull   = 0, # do not pull toward data points
            nudge_y      = 0.5,
            direction    = "x",
            angle        = 90,
            hjust        = 0,
            segment.size = 0.2,
            max.iter = 1e4, max.time = 1
            )+
          scale_fill_gradient(low="steelblue",high = "red",limits = c(20,350),na.value = "grey90")+
          ggtitle(locusData$.id[1])+
          theme_classic()+
          xlab(locusData$locus[1])+
          guides(y="none")+
          ylab("")+
          theme(legend.position="right")
    return(ggplotLGT)
  }
# test the plotting function
plotLGTlocus(dfSplit.filtered[[180]])
# run plotting function over all elements in the dfSplit.filtered list, i.e. over all loci.
list.of.plots<-lapply(dfSplit.filtered,FUN=plotLGTlocus)

# save all plots (adjust path to your system)
#lapply(1:length(list.of.plots), function(i){
      #ggsave(filename=paste0("/Users/lukas/sciebo/Projects/LGT/results/LGT.filtered/",gsub(":","-",names(list.of.plots)[i]),".pdf"), plot=list.of.plots[[i]])
#  })

lapply(1:length(list.of.plots), function(i){
      ggsave(filename=paste0("/global/scratch2/j_rink02/master/lgt/2_analysis/LGT.filtered/",gsub(":","-",names(list.of.plots)[i]),".pdf"), plot=list.of.plots[[i]])
  })

Plot the LGT filters

# Assign names to x
x <- c( "original", "1st_filtering_step", "2nd_filtering_step", "3rd_filtering_step", "merging_of_loci","manual_investigation")
# Assign names to y
y <- c( 13664, 5355, 3631, 3610, 928, 410)
# Assign x to "Filtering_step" as column name
# Assign y to "nr_candidates" as column name
filtering_df <- data.frame( "Filtering_step" = x, "Nr_candidates" = y)
# Print the data frame
filtering_df
library(forcats)

p0 <- ggplot(filtering_df, aes(x=reorder(Filtering_step, -Nr_candidates), y=Nr_candidates, fill=Nr_candidates)) +
  geom_bar(stat="identity")+theme_classic()+
  coord_flip()
p0


# flipped version
p1 <- ggplot(filtering_df, aes(x = reorder(Filtering_step, Nr_candidates), y = Nr_candidates)) +
  geom_bar(aes(fill = as.factor(Nr_candidates)), stat="identity", width = 0.4) + theme_classic() +
  geom_text(aes(label=Nr_candidates), hjust=1.3, color = "white", position=position_dodge(width=1.0), size = 3) +
  scale_fill_gradient(low="darkgreen", high="darkblue") +
  theme(legend.position = "none") +
  coord_flip() +
  theme(plot.title = element_text(hjust = 0.5)) +
  scale_fill_viridis_d(breaks = rev, direction = -1)+
  scale_y_continuous(position = "right", breaks = seq(0,14000, 2000)) +
  labs(title = "Filtering of predicted LGT candidates", x = NULL, y="Number of candidates")
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
p1 


setwd("/Users/Janina/Documents/MASTER/Masterarbeit/plots/self_produced")
The working directory was changed to /Users/Janina/Documents/MASTER/Masterarbeit/plots/self_produced inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
library(export)
graph2ppt(file="LGT_filtering")
Exported graph as LGT_filtering.pptx
ggsave("LGT_filtering.png", path = "/Users/Janina/Documents/MASTER/Masterarbeit/plots/self_produced")
Saving 7 x 7 in image

Last changed 2021/07/19

LS0tCnRpdGxlOiAiUiBOb3RlYm9vazogQW5hbHlzZSBMR1QgY2FuZGlkYXRlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMjIFRPRE8KLSBncm91cCBieSBzdGFydC9lbmQgLSBkb25lIC0KLSBwbG90IGJ5IGxhcmdlIGNhbmRpZGF0ZSAtZG9uZSAtCi0gaW5jbHVkZSBub0FudCAtIGRvbmUgLQotIHNldCB0aHJlc2hvbGRzIGZvciBldmFsdWF0aW9uIG9mIGNhbmRpZGF0ZXMgLSBkb25lIC0KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShkcGx5cikKYGBgCgojIExvYWQgYWxsIGdvb2QgY2FuZGlkYXRlcyBmb3IgYWxsIEdBR0Egc3BlY2llcyAoMXN0IGZpbHRlcjogVGFrZSBvbmx5IGdvb2QgY2FuZGlkYXRlcyBmcm9tIGJvdGggZGF0YWJhc2VzKQoKTG9hZCBhbGwgdHN2IGZpbGVzIGZvciB0aGUgZGlmZmVyZW50IEdBR0EgaWRzIGluIGEgbGlzdCBvZiBkYXRhIGZyYW1lcy4KYGBge3J9CiN0c3YybG9hZDwtIi9Vc2Vycy9sdWthcy9zY2llYm8vUHJvamVjdHMvTEdUL3Jlc3VsdHMvR0FHQS5MR1QvKi9yZXN1bHRzLyouKi5sZ3QuZ29vZC5jYW5kaWRhdGVzLnRzdiIKdHN2MmxvYWQ8LSIvVXNlcnMvSmFuaW5hL3NjaWViby9HQUdBLkxHVC8qL3Jlc3VsdHMvKi4qLmxndC5nb29kLmNhbmRpZGF0ZXMudHN2IgojdHN2MmxvYWQ8LSIvZ2xvYmFsL3NjcmF0Y2gyL2pfcmluazAyL21hc3Rlci9sZ3QvMF9kYXRhLyovcmVzdWx0cy8qLioubGd0Lmdvb2QuY2FuZGlkYXRlcy50c3YiCmRhdGFGaWxlcyA8LSBsYXBwbHkoU3lzLmdsb2IodHN2MmxvYWQpLCByZWFkLmNzdixzZXA9Ilx0IikKYGBgCgojIGFkZCB0aGUgbmFtZQpBZGQgdGhlIEdBR0EgaWQgYXMgYSBuYW1lIHRvIHRoZSBkaWZmZXJlbnQgbGlzdCBlbGVtZW50cy4KQWRkIHRoZSBkYXRhYmFzZSBvcmlnaW4gYXMgYSBuYW1lIHRvIHRoZSBkaWZmZXJlbnQgbGlzdCBlbGVtZW50cy4KU2V0IHRoZSBHQUdBLWlkIGFuZCBkYXRhYmFzZSBvcmlnaW4gc2VwYXJhdGVkIHdpdGggYSAiLiIgYXMgYSBuYW1lIGZvciBlYWNoIGVsZW1lbnQgb2YgdGhlIGxpc3QgImRhdGFGaWxlcyIuCkVhY2ggZWxlbWVudCBpbiB0aGUgbGlzdCBpcyBvbmUgZ2Vub21lLCBlaXRoZXIgZnJvbSB0aGUgbm9BbnQgb3IgdGhlIGV1a2FyeW90aWMgZGF0YWJhc2UgaW5jbHVkaW5nIHRoZSBjYW5kaWRhdGVzLgpgYGB7cn0KaWRzPC1nc3ViKCIuKi8oListWzAtOV0rKVxcLi4rLmxndC5nb29kLmNhbmRpZGF0ZXMudHN2IiwiXFwxIixTeXMuZ2xvYih0c3YybG9hZCkpCnR5cGU8LWdzdWIoIi4qLyguKy1bMC05XSspXFwuKC4rKS5sZ3QuZ29vZC5jYW5kaWRhdGVzLnRzdiIsIlxcMiIsU3lzLmdsb2IodHN2MmxvYWQpKQpuYW1lcyhkYXRhRmlsZXMpPC1wYXN0ZShpZHMsdHlwZSxzZXA9Ii4iKQpgYGAKCgojIENvbWJpbmUgYWxsIEdBR0EgaWRzCkNvbWJpbmUgdHN2IGZpbGVzIGZyb20gYWxsIEdBR0EgaWRzIGluIHRoZSBsYXJnZSBsaXN0IGVsZW1lbnQgKCJkYXRhRmlsZXMiKSB0byBvbmUgYmlnIGRhdGEgZnJhbWUgKCJkZiIpLgpgYGB7cn0KCmRmPC1yYmluZGxpc3QoZGF0YUZpbGVzLGlkY29sID0gVCwgZmlsbD1UUlVFKSAjdGhlIGZpbGw9VFJVRSBvcHRpb24gZmlsbHMgbWlzc2luZyBjb2x1bW5zIApgYGAKClRoZSB1bmZpbHRlcmVkIGRmIGNvbnRhaW5lcyAxMzY2NCBjYW5kaWRhdGVzIGZyb20gYWxsIDE2MiBzZWxlY3RlZCBnZW5vbWVzIHRvZ2V0aGVyLgpFYWNoIHJvdyBpbiB0aGUgZGF0YWZyYW1lIHJlcHJlc2VudHMgb25lIGNhbmRpZGF0ZS4KYGBge3J9CmhlYWQoZGYpCmBgYAoKCiMgQWRkIHNjYWZmb2xkIGxlbmd0aCBmb3IgZXZlcnkgZ2Vub21lCjEuIEFkZCB0aGUgc2NhZmZvbGRzIHdpdGggdGhlaXIgbGVuZ3RocyBmb3IgZXZlcnkgZ2Vub21lIGZyb20gdGhlIGZpbGUgImdlbm9tZS5maWxlIi4KMi4gQ3JlYXRlIGEgbGFyZ2UgZGF0YWZyYW1lIHdpdGggc2NhZmZvbGQgbGVuZ3RoIGluZm9ybWF0aW9uIGZvciBldmVyeSBnZW5vbWUgY2FsbGVkICJkc2NmIgozLiBNZXJnZSB0aGUgc2NhZmZvbGQgaW5mb3JtYXRpb24gdG9nZXRoZXIgd2l0aCB0aGUgaW5mb3JtYXRpb24gZm9yIHRoZSBMR1RzCmBgYHtyfQojZGF0YTJsb2FkPC0iL1VzZXJzL2x1a2FzL3NjaWViby9Qcm9qZWN0cy9MR1QvcmVzdWx0cy9HQUdBLkxHVC8qL3Jlc3VsdHMvZ2Vub21lLmZpbGUiCmRhdGEybG9hZDwtIi9Vc2Vycy9KYW5pbmEvc2NpZWJvL0dBR0EuTEdULyovcmVzdWx0cy9nZW5vbWUuZmlsZSIgICMgbG9hZCBkYXRhIGZyb20gYWxsIGdlbm9tZS5maWxlcwojZGF0YTJsb2FkPC0iL2dsb2JhbC9zY3JhdGNoMi9qX3JpbmswMi9tYXN0ZXIvbGd0LzBfZGF0YS8qL3Jlc3VsdHMvZ2Vub21lLmZpbGUiICAjIGxvYWQgZGF0YSBmcm9tIGFsbCBnZW5vbWUuZmlsZXMKZ2Vub21lRmlsZXMgPC0gbGFwcGx5KFN5cy5nbG9iKGRhdGEybG9hZCksIHJlYWQuY3N2LHNlcD0iXHQiLGhlYWRlcj1GKSAjIGNyZWF0ZSBsYXJnZSBsaXN0IG9mIGFsbCBnZW5vbWUuZmlsZXMKCiMgQ3JlYXRlIGEgbGFyZ2UgZGYgd2l0aCBpbmZvcm1hdGlvbiBvbiBzY2FmZm9sZCBudW1iZXIgYW5kIGxlbmd0aCBmb3IgZXZlcnkgZ2Vub21lCmdlbm9tZV9pZHM8LWdzdWIoIi4qXFwvKC4qKVxcL3Jlc3VsdHNcXC9nZW5vbWUuZmlsZSIsIlxcMSIsU3lzLmdsb2IoZGF0YTJsb2FkKSkKbmFtZXMoZ2Vub21lRmlsZXMpPC1wYXN0ZShnZW5vbWVfaWRzKQoKZHNjZjwtcmJpbmRsaXN0KGdlbm9tZUZpbGVzLGlkY29sID0gVCkKCmNvbG5hbWVzKGRzY2YpCm5hbWVzKGRzY2YpWzJdPC0iY2FuZC5zY2FmZm9sZCIgI3JlbmFtZSB0aGUgc2Vjb25kIGNvbHVtbiBvZiBkc2NmIHRvIG1lcmdlIGxhdGVyIGJ5IHRoaXMgY29sdW1uCm5hbWVzKGRzY2YpWzFdPC0iR0FHQV9pZCIKCmRmJEdBR0FfaWQ8LWdzdWIoIlxcLi4qIiwiIixkZiQuaWQpICNzZXBhcmF0ZSB0aGUgZmlyc3Qgcm93IG9mIGRmIHRvIG1lcmdlIGJ5IGNvbHVtbiAiR0FHQV9pZCIKCgojIE1lcmdlIGRzY2YgdG9nZXRoZXIgd2l0aCB0aGUgaW5mb3JtYXRpb24gZm9yIGV2ZXJ5IExHVCBjYW5kaWRhdGUgZnJvbSBkZgpkZjwtbGVmdF9qb2luKGRmLCBkc2NmLCBieSA9IGMoIkdBR0FfaWQiLCJjYW5kLnNjYWZmb2xkIikpCm5hbWVzKGRmKVs1NV08LSJzY2FmZm9sZC5sZW5ndGgiCmRmJFYyPC0gTlVMTApoZWFkKGRmKQpgYGAKCiMgZXh0cmFjdCBldmFsCjEuIEV4dHJhY3QgdGhlIGV2YWx1ZSBvZiB0aGUgYmVzdCBwcm9rYXJ5b3RpYyBoaXQgKHRha2UgY29sdW1uICJiZXN0UHJvSGl0Ii4gSW4gdGhpcyBjb2x1bW4sIHRoZSBldmFsdWUgaXMgbG9jYXRlZCAgCiAgIGJldHdlZW4gb3RoZXIgdmFsdWVzIGFuZCB3aWxsIGJlIGV4dHJhY3RlZCBpbiB0aGUgbmV4dCBjb21tYW5kKS4KICAgTWFrZSBhIG5ldyBkYXRhIHRhYmxlIChjYWxsZWQgImJlc3RibGFzdGhpdHMiKSwgc2VwYXJhdGluZyB0aGUgb3JpZ2luYWwgY29sdW1uIGJlc3RQcm9IaXQgaW50byBkaWZmZXJlbnQgY29sdW1ucywgICAgd2l0aCB0aGUgZWFjaCB2YWx1ZSBiZWluZyBzZXBhcmF0ZWQgYnkgIjsiLgoKMi4gQWRkIGEgY29sdW1uIGNhbGxlZCAiYmVzdGV2YWwiIHRvIHRoZSBkZiwgdGFraW5nIG9ubHkgdGhlIGV2YWwgKGNvbHVtbiBWNCkgZnJvbSB0aGUgYmVzdGJsYXN0aGl0cyBkYXRhZnJhbWUuCgpgYGB7cn0KYmVzdGJsYXN0aGl0czwtcmVhZC5jc3YodGV4dD1hcy5jaGFyYWN0ZXIoZGYkYmVzdFByb0hpdCksc2VwID0gIjsiLGFzLmlzID0gVCxmaWxsID0gVCxibGFuay5saW5lcy5za2lwID0gRixoZWFkZXI9RikKZGYkYmVzdGV2YWw8LWJlc3RibGFzdGhpdHMkVjQKYGBgCgojIGFkZCBjb2x1bW4gd2l0aCBsZW5ndGggb2YgY2FuZGlkYXRlCgpgYGB7cn0KZGYkY2FuZC5sZW5ndGg8LWRmJGNhbmQuZW5kLWRmJGNhbmQuc3RhcnQKYGBgCiMgZXh0cmFjdCBicm9hZCBsb2N1cyBzdGFydCBhbmQgc3RvcApUbyBjbHVzdGVyIGNhbmRpZGF0ZXMgaW4gb25lIGNsb3NlIHJlZ2lvbiB0b2dldGhlciBhbmQgdmlldyB0aGVtIGFzIG9uZSBzaW5nbGUgTEdULCB0YWtlIHRoZSBicm9hZCBsb2N1cy4KCjEuIFRoZSBicm9hZCBsb2N1cyAoY29sdW1ucyAic2NhZmZvbGQiLCBzdGFydCIsImVuZCIgb3Igc3VtbWFyaXplZCB0b2dldGhlciBpbiBjb2x1bW4gImxvY3VzIiBvZiBkZikgY2x1c3RlcnMgYWxsCiAgIGNhbmRpZGF0ZXMgd2l0aGluICsvLSAyMGtiIHJlZ2lvbiB0b2dldGhlciBhbmQgYXNzaWducyB0aGVtIHRoZSBzYW1lIHN0YXJ0IGFuZCBzdG9wIGNvZG9uLiBUaGVyZWJ5LCBzaW5nbGUKICAgY2FuZGlkYXRlcyBjYW4gYmUgZ3JvdXBlZCBieSB0aGUgc2FtZSBzdGFydCBhbmQgc3RvcCBjb2RvbiBpbiB0aGUgYnJvYWQgbG9jdXMgcmVnaW9uLgogICBTZXBhcmF0ZSB0aGUgY29sdW1uICJsb2N1cyIgZnJvbSB0aGUgZGYgaW50byBzY2FmZm9sZCAoVjEpLCBzdGFydCAoVjIpIGFuZCBlbmQgKFYzKSBpbiB0aGUgbmV3IGRhdGFmcmFtZSAibG9jaSIuCgoyLiBBZGQgYSBjb2x1bW4gY2FsbGVkICJsb2N1cy5zdGFydCIsICJsb2N1cy5lbmQiIGFuZCAibG9jdXMubGVuZ3RoIiB0byB0aGUgZGYsIHRha2luZyB0aGUgbm93IHNlcGFyYXRlZCBjb2x1bW5zCiAgIGZyb20gdGhlIG5ldyBkYXRhZnJhbWUgImxvY2kiLgpgYGB7cn0KbG9jaTwtcmVhZC5jc3YodGV4dD1nc3ViKCI6IiwiLSIsZGYkbG9jdXMpLHNlcCA9ICItIixhcy5pcyA9IFQsZmlsbCA9IFQsYmxhbmsubGluZXMuc2tpcCA9IEYsaGVhZGVyPUYpCmRmJGxvY3VzLnN0YXJ0PC1sb2NpJFYyCmRmJGxvY3VzLmVuZDwtbG9jaSRWMwpkZiRsb2N1cy5sZW5ndGg8LWRmJGxvY3VzLmVuZC1kZiRsb2N1cy5zdGFydApgYGAKCiMgcGxvdCBvdmVydmlldyBwbG90cwpwbG90IGhpc3RvZ3JhbXMgb2YgZGlmZmVyZW50IG1ldHJpY3MgdG8gc2VlIGhvdyB0aGV5IGFyZSBkaXN0cmlidXRlZC4KYGBge3J9CiMgZ2MtY29udGVudApnZ3Bsb3QoZGYsIGFlcyh4PWdjKSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9InJlZCIsIGFscGhhPTAuNSwgcG9zaXRpb249ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpCgojIGVudHJvcHkgKGNlKQpnZ3Bsb3QoZGYsIGFlcyh4PWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9InJlZCIsIGFscGhhPTAuNSwgcG9zaXRpb249ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpCgojIGN0NApnZ3Bsb3QoZGYsIGFlcyh4PWN0NCkpICsKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJyZWQiLCBhbHBoYT0wLjUsIHBvc2l0aW9uPSJpZGVudGl0eSIpK3RoZW1lX2NsYXNzaWMoKQoKIyBjdDYKZ2dwbG90KGRmLCBhZXMoeD1jdDYpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIiwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSt0aGVtZV9jbGFzc2ljKCkKCiMgbGVuZ3RoIG9mIGJyb2FkIGxvY3VzCmdncGxvdChkZiwgYWVzKHg9bG9jdXMubGVuZ3RoKSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9InJlZCIsIGFscGhhPTAuNSwgcG9zaXRpb249ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpCgojIGxlbmd0aCBvZiBjYW5kaWRhdGUKZ2dwbG90KGRmLCBhZXMoeD1jYW5kLmxlbmd0aCkpICsKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJyZWQiLCBhbHBoYT0wLjUsIHBvc2l0aW9uPSJpZGVudGl0eSIpK3RoZW1lX2NsYXNzaWMoKQoKIyBsb2cgb2YgYmVzdCBlLXZhbHVlCmRmJGxvZy5iZXN0ZXZhbDwtIC1sb2coZGYkYmVzdGV2YWwsMTApCmRmJGxvZy5iZXN0ZXZhbFtkZiRsb2cuYmVzdGV2YWw9PUluZl08LW1heChkZiRsb2cuYmVzdGV2YWxbZGYkbG9nLmJlc3RldmFsIT1JbmZdKSsxCmdncGxvdChkZiwgYWVzKHg9ZGYkbG9nLmJlc3RldmFsKSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9InJlZCIsIGFscGhhPTAuNSwgcG9zaXRpb249ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpCgojIEJpdERpZmZTdW0KZ2dwbG90KGRmLCBhZXMoeD1CaXREaWZmU3VtKSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9InJlZCIsIGFscGhhPTAuNSwgcG9zaXRpb249ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpCmBgYAoKCiMgRmlsdGVyIHRoZSBjYW5kaWRhdGUgZGF0YXNldCB0byBleGNsdWRlIGZhbHNlLXBvc2l0aXZlcyBhbmQgbWlzc2Fzc2VtYmxpZXMKCkZpbHRlciBieSBldmFsdWUgKGV2YWx1ZSA+IDFlLTI1KSwgY3Q0IChjdDQ+MC4yNSksIGNlIChjZT4xLjUpLCBCaXREaWZmU3VtIChCaXREaWZmU3VtPjE1MCkgYW5kIHRoZSBsZW5ndGggb2YgdGhlIGNhbmRpZGF0ZSAobGVuZ3RoPjEwMCkKCmBgYHtyfQpkZkZpbHRlcjwtc3Vic2V0KGRmLGxvZy5iZXN0ZXZhbD4yNSAmIGN0ND4wLjI1ICYgY2U+MS41ICYgQml0RGlmZlN1bT4xNTAgJiBjYW5kLmVuZC1jYW5kLnN0YXJ0PjEwMCkKYGBgCkZpbHRlcmluZyBieSB0aGUgYWJvdmUgY3JpdGVyaWEgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIGNhbmRpZGF0ZXMgdG8gNTM1NSBjYW5kaWRhdGVzIChvcmlnaW5hbGx5OiAxMzY2NCkgaW4gYWxsIDE2MiBnZW5vbWVzLgoKClJlbW92ZSBhbnl0aGluZyBhdCB0aGUgYmVnaW5uaW5nIG9mIGEgc2NhZmZvbGQgYW5kIGNhbmRpZGF0ZXMgd2l0aCBhIGxlbmd0aCBvdmVyIDIwIGtiLgpUaGVzZSBhcmUgbW9yZSBsaWtlbHkgdG8gYmUgbWlzYXNzZW1ibGllcy4gKE5vdGUgdGhhdCB0aGlzIHN0aWxsIGxlYXZlcyBpbiBjYW5kaWRhdGVzIGF0IHRoZSB2ZXJ5IGVuZCBvZiBzY2FmZm9sZHMsIHdoaWNoIGFsc28gYXJlIG1vcmUgbGlrZWx5IG1pc2Fzc2VtYmxpZXMpLgpgYGB7cn0KZGZGaWx0ZXIyPC1zdWJzZXQoZGZGaWx0ZXIsbG9jdXMuc3RhcnQgPiAxMDAwICYgY2FuZC5sZW5ndGggPCAyMDAwMCkKYGBgCkZpbHRlcmluZyBieSB0aGUgYWJvdmUgY3JpdGVyaWEgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIGNhbmRpZGF0ZXMgZnVydGhlciB0byAzNjMxIGNhbmRpZGF0ZXMgaW4gYWxsIDE2MiBnZW5vbWVzLgoKUmVtb3ZlIGFueXRoaW5nIGF0IHRoZSBlbmQgb2YgYSBzY2FmZm9sZC4KYGBge3J9CiMgRmlsdGVyIHRoZSBkYXRhZnJhbWUgdG8gcmVtb3ZlIGNhbmRpZGF0ZXMgYXQgdGhlIGVuZCBvZiB0aGUgc2NhZmZvbGQgICAgICAgCmRmRmlsdGVyMzwtc3Vic2V0KGRmRmlsdGVyMiwgY2FuZC5lbmQgIT0gc2NhZmZvbGQubGVuZ3RoKQpgYGAKVGhpcyBmdXJ0aGVyIHJlZHVjZXMgdGhlIG51bWJlciBvZiBjYW5kaWRhdGVzIHRvIDM2MTAgcG9zc2libGUgTEdUcyBpbiBhbGwgMTYyIGdlbm9tZXMuCgoKIyBTdW1tYXJpemUgY2FuZGlkYXRlcyBpbiBjbG9zZSBwcm94aW1pdHkgaW50byBvbmUgbGFyZ2VyIGxvY3VzCk9uZSByb3cgcGVyIGJyb2FkIHN0YXJ0L3N0b3AgY29vcmRpbmF0ZQoKIyMgcmVxdWlyZWQgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCmBgYAoKMS4gQ3JlYXRlIGEgZGF0YSBmcmFtZSB0aGF0IGhhcyBpbiBlYWNoIHJvdyBvbmUgbGFyZ2VyIGxvY3VzIChvZnRlbiBjb250YWluaW5nIHNldmVyYWwgbGd0IGNhbmRpZGF0ZXMpLgpgYGB7cn0KIyBrZWVwIG9uZSByb3cgcGVyIGxhcmdlciBsb2N1cyBhbmQgcGFzdGUgdG9nZXRoZXIgYWxsIHRoZSBpbmZvIGZvciB0aGUgZGlmZmVyZW50IExHVCBjYW5kaWRhdGVzIGNvbnRhaW5lZCBpbiB0aGlzIGxvY3VzCgojIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80MDAzMzYyNS9jb25jYXRlbmF0aW5nLWFsbC1yb3dzLXdpdGhpbi1hLWdyb3VwLXVzaW5nLWRwbHlyLzQwMDMzNzI1CmRmQyA8LSBkcGx5cjo6Z3JvdXBfYnkoZGYsIGxvY3VzKSAlPiUKICAgICAgICBkcGx5cjo6c3VtbWFyaXNlX2VhY2goZnVucyhwYXN0ZSh1bmlxdWUoLiksIGNvbGxhcHNlID0gIjsiKSkpCgojIGZpbHRlciB0aGlzIGJ5IGxvY3VzIGRhdGFmcmFtZSB0byBvbmx5IGtlZXAgbG9jaSB0aGF0IGhhdmUgYXQgbGVhc3Qgb25lIGdvb2QgY2FuZGlkYXRlIChpLmUuIHRoYXQgd2FzIGNvbnRhaW5lZCBpbiB0aGUgZGZGaWx0ZXIzIGRhdGFmcmFtZSkKZGZDLmZpbHRlcmVkPC1zdWJzZXQoZGZDLGxvY3VzICVpbiUgdW5pcXVlKGRmRmlsdGVyMyRsb2N1cyksc2VsZWN0PWMobG9jdXMsLmlkLGNhbmQubG9jdXMsY2FuZC5zdGFydCxjYW5kLmVuZCxiZXN0UHJvSGl0LEJpdERpZmZTdW0sc2NhZmZvbGQsc3RhcnQsZW5kLGdjLGdjcyxjZSkpCgojIHNhdmUgZGF0YSBmcmFtZSB0byBmaWxlCiN3cml0ZS50YWJsZShkZkMuZmlsdGVyZWQsIi9Vc2Vycy9sdWthcy9zY2llYm8vUHJvamVjdHMvTEdUL3Jlc3VsdHMvR0FHQS5MR1QuZmlsdGVyZWQudHN2IixzZXA9Ilx0IixxdW90ZSA9IEYscm93Lm5hbWVzID0gRikKd3JpdGUudGFibGUoZGZDLmZpbHRlcmVkLCIvZ2xvYmFsL3NjcmF0Y2gyL2pfcmluazAyL21hc3Rlci9sZ3QvMl9hbmFseXNpcy9HQUdBLkxHVC4xNjIuZmlsdGVyZWQudHN2IixzZXA9Ilx0IixxdW90ZSA9IEYscm93Lm5hbWVzID0gRikKCiMgZm9yIHBsb3R0aW5nCiMgc3BsaXQgdGhlIHVuZmlsdGVyZWQgbGFyZ2UgZGYgZGF0YWZyYW1lIGJ5ICJsb2N1cyIgaW50byBhIGxpc3Qgb2YgZGF0YWZyYW1lcyAob25lIGxpc3QgZWxlbWVudCBwZXIgbG9jdXMpCmRmU3BsaXQ8LXNwbGl0KGRmLGY9cGFzdGUoZGYkLmlkLGRmJGxvY3VzLHNlcD0iLiIpKQoKIyBmaWx0ZXIgdGhlIGxpc3Qgb2YgZGF0YWZyYW1lcyB0byBvbmx5IHJldGFpbiB0aG9zZSB0aGF0IGNvbnRhaW4gYSBMR1QgZnJvbSB0aGUgZGZGaWx0ZXIzIGRhdGFmcmFtZQpkZlNwbGl0LmZpbHRlcmVkPC1kZlNwbGl0W3VuaXF1ZShwYXN0ZShkZkZpbHRlcjMkLmlkLGRmRmlsdGVyMyRsb2N1cyxzZXA9Ii4iKSldCgoKYGBgCk1lcmdpbmcgY2FuZGlkYXRlcyBpbiBjbG9zZSBwcm94aW1pdHkgdG9nZXRoZXIgYW5kIGZpbHRlcmluZyBieSB0aGUgYWJvdmUgY3JpdGVyaWEgdG8gb25seSBvYnRhaW4gY2FuZGlkYXRlcyB3aGljaCBhcmUgYWxzbyBpbiB0aGUgZGZGaWx0ZXIzIGRhdGFmcmFtZSByZWR1Y2VzIHRoZSBudW1iZXIgb2YgY2FuZGlkYXRlcyBmdXJ0aGVyIHRvIDkyOCBjYW5kaWRhdGVzIGluIGFsbCAxNjIgZ2Vub21lcy4KCk5vdGU6IFRoZSBmaWx0ZXJlZC50c3YgZmlsZSBvbmx5IGNvbnRhaW5zIGNhbmRpZGF0ZXMsIHdoaWNoIHJlbWFpbiBhZnRlciBmaWx0ZXJpbmcsIGJ1dCB0aGUgc2ltcGxlIHBsb3RzIGNyZWF0ZWQgYnkgdGhlIGFib3ZlIGNvbW1hbmRzIGNvbnRhaW4gYWxsIGNhbmRpZGF0ZXMgaW4gdGhpcyByZWdpb24sIGV2ZW4gaWYgdGhleSB3b3VsZCBoYXZlIGJlZW4gZmlsdGVyZWQgb3V0IGJlZm9yZS4KCiMgUGxvdCBlYWNoIGxvY3VzCgpgYGB7cn0KIyBEZWZpbmUgYSBmdW5jdGlvbiBjb250YWluaW5nIGEgZ2dwbG90IGNvbW1hbmQuIFRoaXMgZnVuY3Rpb24gd2lsbCBiZSBhcHBsaWVkIHRvIGVhY2ggZWxlbWVudCBvZiBkZlNwbGl0LmZpbHRlcmVkICh0aGUgbGlzdCBvZiBkYXRhZnJhbWVzKQpwbG90TEdUbG9jdXM8LWZ1bmN0aW9uKGxvY3VzRGF0YSl7CiAgICBsb2N1c0RhdGEkbG9nZXZhbDwtIC1sb2cobG9jdXNEYXRhJGJlc3RldmFsLDEwKQogICAgbG9jdXNEYXRhJGxvZ2V2YWxbIWlzLmZpbml0ZShsb2N1c0RhdGEkbG9nZXZhbCldPC0gMzUwCiAgICBsb2N1c0RhdGEkc3BlY2llczwtc3Vic3RyKGdzdWIoIi4qOyIsIiIsbG9jdXNEYXRhJGJlc3RQcm9IaXQpLDEsMjApCiAgICBnZ3Bsb3RMR1Q8LWdncGxvdChsb2N1c0RhdGEpKwogICAgICAgICAgZ2VvbV9yZWN0KGFlcyh4bWluPWNhbmQuc3RhcnQseG1heD1jYW5kLmVuZCx5bWluPTEseW1heD0wLGZpbGw9bG9nZXZhbCksc2l6ZT0wKSsKICAgICAgICAgIGNvb3JkX2NhcnRlc2lhbih4bGltPWMobWluKGxvY3VzRGF0YSRsb2N1cy5zdGFydCksbWF4KGxvY3VzRGF0YSRsb2N1cy5lbmQpKSx5bGltPWMoMCw1KSkrCiAgICAgICAgICBnZW9tX3RleHRfcmVwZWwoCiAgICAgICAgICAgIGFlcyh4PWNhbmQuc3RhcnQseT0xLGxhYmVsPXNwZWNpZXMpLAogICAgICAgICAgICBmb3JjZV9wdWxsICAgPSAwLCAjIGRvIG5vdCBwdWxsIHRvd2FyZCBkYXRhIHBvaW50cwogICAgICAgICAgICBudWRnZV95ICAgICAgPSAwLjUsCiAgICAgICAgICAgIGRpcmVjdGlvbiAgICA9ICJ4IiwKICAgICAgICAgICAgYW5nbGUgICAgICAgID0gOTAsCiAgICAgICAgICAgIGhqdXN0ICAgICAgICA9IDAsCiAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuMiwKICAgICAgICAgICAgbWF4Lml0ZXIgPSAxZTQsIG1heC50aW1lID0gMQogICAgICAgICAgICApKwogICAgICAgICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsaGlnaCA9ICJyZWQiLGxpbWl0cyA9IGMoMjAsMzUwKSxuYS52YWx1ZSA9ICJncmV5OTAiKSsKICAgICAgICAgIGdndGl0bGUobG9jdXNEYXRhJC5pZFsxXSkrCiAgICAgICAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICAgICAgICB4bGFiKGxvY3VzRGF0YSRsb2N1c1sxXSkrCiAgICAgICAgICBndWlkZXMoeT0ibm9uZSIpKwogICAgICAgICAgeWxhYigiIikrCiAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikKICAgIHJldHVybihnZ3Bsb3RMR1QpCiAgfQpgYGAKCmBgYHtyfQojIHRlc3QgdGhlIHBsb3R0aW5nIGZ1bmN0aW9uCnBsb3RMR1Rsb2N1cyhkZlNwbGl0LmZpbHRlcmVkW1sxODBdXSkKYGBgCgoKYGBge3J9CiMgcnVuIHBsb3R0aW5nIGZ1bmN0aW9uIG92ZXIgYWxsIGVsZW1lbnRzIGluIHRoZSBkZlNwbGl0LmZpbHRlcmVkIGxpc3QsIGkuZS4gb3ZlciBhbGwgbG9jaS4KbGlzdC5vZi5wbG90czwtbGFwcGx5KGRmU3BsaXQuZmlsdGVyZWQsRlVOPXBsb3RMR1Rsb2N1cykKCiMgc2F2ZSBhbGwgcGxvdHMgKGFkanVzdCBwYXRoIHRvIHlvdXIgc3lzdGVtKQojbGFwcGx5KDE6bGVuZ3RoKGxpc3Qub2YucGxvdHMpLCBmdW5jdGlvbihpKXsKICAgICAgI2dnc2F2ZShmaWxlbmFtZT1wYXN0ZTAoIi9Vc2Vycy9sdWthcy9zY2llYm8vUHJvamVjdHMvTEdUL3Jlc3VsdHMvTEdULmZpbHRlcmVkLyIsZ3N1YigiOiIsIi0iLG5hbWVzKGxpc3Qub2YucGxvdHMpW2ldKSwiLnBkZiIpLCBwbG90PWxpc3Qub2YucGxvdHNbW2ldXSkKIyAgfSkKCmxhcHBseSgxOmxlbmd0aChsaXN0Lm9mLnBsb3RzKSwgZnVuY3Rpb24oaSl7CiAgICAgIGdnc2F2ZShmaWxlbmFtZT1wYXN0ZTAoIi9nbG9iYWwvc2NyYXRjaDIval9yaW5rMDIvbWFzdGVyL2xndC8yX2FuYWx5c2lzL0xHVC5maWx0ZXJlZC8iLGdzdWIoIjoiLCItIixuYW1lcyhsaXN0Lm9mLnBsb3RzKVtpXSksIi5wZGYiKSwgcGxvdD1saXN0Lm9mLnBsb3RzW1tpXV0pCiAgfSkKYGBgCgoKClBsb3QgdGhlIExHVCBmaWx0ZXJzIApgYGB7cn0KIyBBc3NpZ24gbmFtZXMgdG8geAp4IDwtIGMoICJvcmlnaW5hbCIsICIxc3RfZmlsdGVyaW5nX3N0ZXAiLCAiMm5kX2ZpbHRlcmluZ19zdGVwIiwgIjNyZF9maWx0ZXJpbmdfc3RlcCIsICJtZXJnaW5nX29mX2xvY2kiLCJtYW51YWxfaW52ZXN0aWdhdGlvbiIpCiMgQXNzaWduIG5hbWVzIHRvIHkKeSA8LSBjKCAxMzY2NCwgNTM1NSwgMzYzMSwgMzYxMCwgOTI4LCA0MTApCiMgQXNzaWduIHggdG8gIkZpbHRlcmluZ19zdGVwIiBhcyBjb2x1bW4gbmFtZQojIEFzc2lnbiB5IHRvICJucl9jYW5kaWRhdGVzIiBhcyBjb2x1bW4gbmFtZQpmaWx0ZXJpbmdfZGYgPC0gZGF0YS5mcmFtZSggIkZpbHRlcmluZ19zdGVwIiA9IHgsICJOcl9jYW5kaWRhdGVzIiA9IHkpCiMgUHJpbnQgdGhlIGRhdGEgZnJhbWUKZmlsdGVyaW5nX2RmCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGZvcmNhdHMpCgpwMCA8LSBnZ3Bsb3QoZmlsdGVyaW5nX2RmLCBhZXMoeD1yZW9yZGVyKEZpbHRlcmluZ19zdGVwLCAtTnJfY2FuZGlkYXRlcyksIHk9TnJfY2FuZGlkYXRlcywgZmlsbD1Ocl9jYW5kaWRhdGVzKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpKwogIGNvb3JkX2ZsaXAoKQpwMAoKIyBmbGlwcGVkIHZlcnNpb24KcDEgPC0gZ2dwbG90KGZpbHRlcmluZ19kZiwgYWVzKHggPSByZW9yZGVyKEZpbHRlcmluZ19zdGVwLCBOcl9jYW5kaWRhdGVzKSwgeSA9IE5yX2NhbmRpZGF0ZXMpKSArCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBhcy5mYWN0b3IoTnJfY2FuZGlkYXRlcykpLCBzdGF0PSJpZGVudGl0eSIsIHdpZHRoID0gMC40KSArIHRoZW1lX2NsYXNzaWMoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1Ocl9jYW5kaWRhdGVzKSwgaGp1c3Q9MS4zLCBjb2xvciA9ICJ3aGl0ZSIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTEuMCksIHNpemUgPSAzKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9ImRhcmtncmVlbiIsIGhpZ2g9ImRhcmtibHVlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChicmVha3MgPSByZXYsIGRpcmVjdGlvbiA9IC0xKSsKICBzY2FsZV95X2NvbnRpbnVvdXMocG9zaXRpb24gPSAicmlnaHQiLCBicmVha3MgPSBzZXEoMCwxNDAwMCwgMjAwMCkpICsKICBsYWJzKHRpdGxlID0gIkZpbHRlcmluZyBvZiBwcmVkaWN0ZWQgTEdUIGNhbmRpZGF0ZXMiLCB4ID0gTlVMTCwgeT0iTnVtYmVyIG9mIGNhbmRpZGF0ZXMiKQpwMSAKCnNldHdkKCIvVXNlcnMvSmFuaW5hL0RvY3VtZW50cy9NQVNURVIvTWFzdGVyYXJiZWl0L3Bsb3RzL3NlbGZfcHJvZHVjZWQiKQpsaWJyYXJ5KGV4cG9ydCkKZ3JhcGgycHB0KGZpbGU9IkxHVF9maWx0ZXJpbmciKQpgYGAKYGBge3J9CgpnZ3NhdmUoIkxHVF9maWx0ZXJpbmcucG5nIiwgcGF0aCA9ICIvVXNlcnMvSmFuaW5hL0RvY3VtZW50cy9NQVNURVIvTWFzdGVyYXJiZWl0L3Bsb3RzL3NlbGZfcHJvZHVjZWQiKQpgYGAKTGFzdCBjaGFuZ2VkIDIwMjEvMDcvMTkK